import re
import unittest
class CronType():
    def __init__(self,strVal):
        self.strVal=strVal

NONE = CronType('none')
def noneStr(values,aux):
    return ''
NONE.toStr=noneStr

EVERY = CronType('every')
def everyStr(values,aux):
    return 'every'+aux
EVERY.toStr=everyStr

FROMTO = CronType('fromto')
def fromtoStr(values,aux):
    return 'from '+values[0]+' to '+values[1]+aux
FROMTO.toStr =fromtoStr
EACH = CronType('each')
def eachStr(values,aux):
    end=' '
    if int(values[0])>1:
        end='s '
    return 'each '+values[0]+aux+end
EACH.toStr = eachStr
AT = CronType('at')
def atStr(values,aux): 
    return 'at '+','.join(values)+aux
AT.toStr =atStr
ATEACH = CronType('ateach')
def ateachStr(values,aux):
    return 'at '+values[0]+ ' each '+values[1]+aux
ATEACH.toStr =ateachStr 
FROMTOEACH = CronType('fromtoeach')
def fromtoeachStr(values,aux):
    return 'from  '+values[0]+ ' to '+values[1]+' each '+values[2]+aux
FROMTOEACH.toStr =fromtoeachStr

        


class CronPart():
    
    def __init__(self,crontype=None,values=None):
        self.crontype=crontype
        self.values=values
    def __str__(self):
        return self.crontype.toStr(self.values,' minutes')
        

def parseMinute(str):
    if str=='?':
        return CronPart(NONE)
    elif str=='*':
        return CronPart(EVERY)
    elif re.match(r'\d+(,\d+)*$',str):
        nums = str.split(',')
        return CronPart(AT,nums)
    elif re.match(r'\d+/\d+$',str):
        nums = str.split('/')
        return CronPart(ATEACH,nums)
    elif re.match(r'\d+\-\d+$',str):
        nums = str.split('-')
        return CronPart(FROMTO,nums)
    elif re.match(r'\d+\-\d+/\d+$',str):
        nums = str.split('-')
        nums2 =nums[1].split('/')
        return CronPart(FROMTOEACH,[nums[0],nums2[0]],nums2[1])
    else:
        raise RuntimeError('could not parse expression:'+str)


def _transformDayMonth(dayPart,monthPart):
    res=''
    if monthPart.crontype==EVERY and dayPart.crontype==EVERY:
            res+='every day '
    else:
        if dayPart.crontype==AT:
            res+='on '+','.join(map(lambda(x): x+'th',dayPart.values))+' '
        elif FROMTO==dayPart.crontype:
            res+='from the '+dayPart.values[0]+'th to '+dayPart.values[1]+'th '
    res = res.replace(' 1th', ' 1st').replace(' 2th',' 2nd').replace(' 3th',' 3rd')
    res = res.replace(',1th', ',1st').replace(',2th',',2nd').replace(',3th',',3rd')
    return res

def _transformMinute(minutePart):
    return minutePart.crontype.toStr(minutePart.values,' minutes')

months=['January','February','March','April','May','June','July','August','September','October','November','December']
def _transformMonth(monthPart):
    res=''
    if monthPart.crontype==EVERY:
        res+='of every month '
    elif monthPart.crontype==AT:
        res+='of '+','.join(map(lambda(x):months[int(x)-1],monthPart.values))+' '
    elif monthPart.crontype==FROMTO:
        res+='from '+months[int(monthPart.values[0])-1]+' to '+months[int(monthPart.values[1])-1]+' '
    return res

def _transform(minutePart,hourPart,dayPart,monthPart,weekPart):
    """Function wich take all the CronPart's extracted from a expression, 
    and translate it to English.
    """
    res = ''
    if hourPart.crontype==EVERY:
        if minutePart.crontype==EVERY:
            res+='every minute '
        else:
            res+=_transformMinute(minutePart)+' every hour '
    elif hourPart.crontype==AT and len(hourPart.values)==1:
        if minutePart.crontype==AT and len(minutePart.values)==1:
            res+=' at '+('00'+hourPart.values[0])[-2:]+':'+('00'+minutePart.values[0])[-2:]+' '
    else:
        res+=hourPart.crontype.toStr(hourPart.values,' hour')
        res+=minutePart.crontype.toStr(minutePart.values,' minutes')
    res+=_transformDayMonth(dayPart, monthPart)
    if dayPart.crontype!=EVERY or weekPart.crontype==NONE:
        res+=_transformMonth(monthPart)
    return res.strip()



def cronParse(str):
    """Translates from cron expression to english.
    """
    divs = str.split()
    minutePart = parseMinute(divs[0])
    hourPart = parseMinute(divs[1])
    dayPart = parseMinute(divs[2])
    monthPart = parseMinute(divs[3])
    weekPart = parseMinute(divs[4])
    
    return _transform(minutePart,hourPart,dayPart,monthPart,weekPart)
    
    
    
    
class MinuteTest(unittest.TestCase):
    def testMinuteParse(self):
        self.assertEquals(str(parseMinute('0/30')),'at 0 each 30 minutes')
        self.assertEquals(str(parseMinute('*')),'every minutes')
        self.assertEquals(str(parseMinute('9-12')),'from 9 to 12 minutes')
        self.assertEquals(str(parseMinute('12,13,15')),'at 12,13,15 minutes')
        
        
    testCases = [('0 0 12 * * ?','at 00:00 on 12th of every month'),('0 0 * * * ?','at 00:00 every day'),
                 ('0 0 10-20 * * ?','at 00:00 from the 10th to 20th of every month'),
                 ('* * 10-20 * * ?','every minute from the 10th to 20th of every month'),
                 ('0 * 10 2-5 * ?','at 0 minutes every hour on 10th from February to May'),
                 ('0 0 1,2,3 1 * ?','at 00:00 on 1st,2nd,3rd of January'),
                 ('0 3/6 * * * ?','at 3 each 6 hours at 0 minutes every day')
                 ]
    def testOkCases(self):
        for case in self.testCases:
            self.assertEquals(cronParse(case[0]),case[1])
            
    
    
